﻿using System;

namespace LA6
{
    /// <summary>
    /// The class used to store nodes in a linked list.
    /// </summary>
    class LinkedList
    {
        private ListNode firstNode;
        private ListNode lastNode;

        /// <summary>
        /// The default constructor that sets the first and last node values to null.
        /// </summary>
        public LinkedList()
        {
            firstNode = null;
            lastNode = null;
        }

        // Methods: ---------------------------------------------------------------

        /// <summary>
        /// Inserts a node with the given name at the front of the list.
        /// </summary>
        /// <param name="nameValue">The name of the node to insert.</param>
        public void InsertAtFront(string nameValue)
        {
            if (firstNode == null && lastNode == null)
            {
                firstNode = new ListNode(nameValue, null, null);
                lastNode = firstNode;
            }
            else
            {
                firstNode = new ListNode(nameValue, firstNode, null);
            }
        }

        /// <summary>
        /// Inserts a node with the given name and replacement rules at the front of the list.
        /// </summary>
        /// <param name="nameValue">The name of the node to insert</param>
        /// <param name="rulesValue">The array of rules that replace the name.</param>
        public void InsertAtFront(string nameValue, string[] rulesValue)
        {
            if (firstNode == null && lastNode == null)
            {
                firstNode = new ListNode(nameValue, rulesValue, null, null);
                lastNode = firstNode;
            }
            else
            {
                firstNode = new ListNode(nameValue, rulesValue, firstNode, null);
            }
        }

        /// <summary>
        /// Inserts a node with the given name at the back of the list.
        /// </summary>
        /// <param name="nameValue">The name of the node to insert.</param>
        public void InsertAtBack(string nameValue)
        {
            if (firstNode == null && lastNode == null)
            {
                firstNode = new ListNode(nameValue, null, null);
                lastNode = firstNode;
            }
            else
            {
                lastNode.NextNode = new ListNode(nameValue, null, lastNode);
                lastNode = lastNode.NextNode;
            }
        }

        /// <summary>
        /// Inserts a node with the given name at the back of the list.
        /// </summary>
        /// <param name="nameValue">The name of the node to insert.</param>
        /// <param name="rulesValue">The array of rules that replace the name.</param>
        public void InsertAtBack(string nameValue, string[] rulesValue)
        {
            if (firstNode == null && lastNode == null)
            {
                firstNode = new ListNode(nameValue, rulesValue, null, null);
                lastNode = firstNode;
            }
            else
            {
                lastNode.NextNode = new ListNode(nameValue, rulesValue, null, lastNode);
                lastNode = lastNode.NextNode;
            }
        }

        /// <summary>
        /// Removes the first node in the list.
        /// </summary>
        /// <returns>The node that was removed from the list.</returns>
        public ListNode RemoveFromFront()
        {
            ListNode removedItem = firstNode;

            if (firstNode == lastNode)
            {
                firstNode = null;
                lastNode = null;
            }
            else
            {
                firstNode = firstNode.NextNode;
                firstNode.PreviousNode = null;
            }

            return removedItem;
        }

        /// <summary>
        /// Removes the last node in the list.
        /// </summary>
        /// <returns>The node that was removed from the list.</returns>
        public ListNode RemoveFromBack()
        {
            ListNode removedItem = lastNode;

            if (firstNode == lastNode)
            {
                firstNode = null;
                lastNode = null;
            }
            else
            {
                lastNode.PreviousNode.NextNode = null;
            }

            return removedItem;
        }

        /// <summary>
        /// Searches the list for a matching name and randomly chooses a matching rule to replace it.
        /// </summary>
        /// <param name="name">The name to search for through the list.</param>
        /// <returns>A randomly chosen rule that replaces the name.</returns>
        public string[] Search(string name)
        {
            ListNode node = firstNode;
            int counter = 0;
            string[] rule = new string[0];

            // Count how many rules exist in the list that match the parameter
            while (node != null)
            {
                if (node.Name == name)
                    counter++;
                node = node.NextNode;
            }

            if (counter == 0)
                rule = null;

            // If there is only one rule, return that rule
            else if (counter == 1)
            {
                node = firstNode;

                while (node != null)
                {
                    if (node.Name == name)
                        rule = node.Rules;
                    node = node.NextNode;
                }
            }

            // If there are multiple rules, return a random rule
            else
            {
                int random = RandomHelper.Next(1, counter + 1);
                node = firstNode;

                do
                {
                    if (node.Name == name)
                    {
                        random -= 1;

                        if (random == 0)
                            rule = node.Rules;
                    }

                    node = node.NextNode;
                }
                while (random != 0);
            }
            return rule;
        }

        /// <summary>
        /// Clears the list of all elements.
        /// </summary>
        public void Clear()
        {
            while (firstNode != null)
                RemoveFromFront();
        }

        /// <summary>
        /// Creates a string representation of the list.
        /// </summary>
        /// <returns>The string representatio of the list.</returns>
        public override string ToString()
        {
            string result = "";
            ListNode node = firstNode;

            while (node != null)
            {
                if (node.Rules != null)
                    result += node.Name.ToString() + " -> ";

                foreach (string s in node.Rules)
                {
                    result += s + " ";
                }
                result += "\r\n";

                node = node.NextNode;
            }

            return result;
        }
    }
}